﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Text;

namespace Utils
{
    public class CommonUtilities
    {
        public static string GetTitleInUrl(string title)
        { 
            title = title.ToLower();
            Char[] ValidChars = { '0', '1', '2', '3', '4', '5','6','7','8','9',
                                'a', 'b', 'c', 'd', 'e', 'f', 'g','h','i','j','k',
                                'l', 'm', 'n', 'o', 'p', 'q','r','s','t','u',
                                'v', 'w', 'x', 'y', 'z'};

            string result = "";
            Boolean isvalid = true;
            for (int i = 0; i < title.Length; i++)
            {
                if (ValidChars.Contains(title[i]))
                {
                    isvalid = true;
                    result = result + title[i].ToString();
                }
                else
                {
                    if (isvalid)
                    {
                        result = result + "-";
                    }
                    isvalid = false;
                }
            }
            if (result[result.Length - 1] == '-')
            {
                result = result.Substring(0, result.Length - 1);
            }
            if (result.Length > 1)
            {
                if (result[0] == '-')
                {
                    result = result.Substring(1, result.Length - 1);
                }
            }
            return result;
        }

        public static string GetShareToFriendUrlString(string link, string title)
        {
            string template = "?subject={0}&body={1}";
            string bodyTemplate = 
                "I found this job on JuicyBasket.com: \n\n{0}\n{1}";
            string subject = HttpUtility.UrlEncode(title);
            string body = HttpUtility.UrlEncode(string.Format(bodyTemplate, title, link));
            return string.Format(template, subject, body);
        }

        public static string GetShareToTwitterUrlString(string link, string title)
        {
            string template = "https://twitter.com/intent/tweet?url={0}&via={1}&text={2}";
            return string.Format(template, HttpUtility.UrlEncode(link), "JBCareers", HttpUtility.UrlEncode(title));
        }

        public static string GetShareToFacebookUrlString(string link, string title)
        {
            string template = "http://www.facebook.com/sharer/sharer.php?u={0}&t={1}";
            return string.Format(template, HttpUtility.UrlEncode(link), HttpUtility.UrlEncode(title));
        }

        public static IEnumerable<T> GetEnumValues<T>()
        {
            return Enum.GetValues(typeof(T)).Cast<T>();
        }

        public static int GetNextPage(int totalCount, int currentPage, int numPerPage)
        {
            int currentPageEnd = currentPage * numPerPage;
            if (totalCount > currentPageEnd)
            {
                return currentPage + 1;
            }
            else
            {
                return -1;
            }
        }

        public static int GetPreviousPage(int currentPage)
        {
            if (currentPage > 1)
            {
                return currentPage - 1;
            }
            else
            {
                return -1;
            }
        }

        /// <summary>
        /// build string array from a string which separated by delimiter  e.g. [Name],[EmailAddress],[Url]
        /// </summary>
        /// <param name="bodyParameter"></param>
        /// <returns></returns>
        public static string[] GetStringArray(string str, string delimiter)
        {
            if (!string.IsNullOrEmpty(str))
            {
                //split by ","
                string[] parameters = str.Split(new string[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
                //trimming for each
                parameters = parameters.Select(x => x.Trim()).ToArray();
                return parameters;
            }
            else
            {
                return null;
            }
        }

        public static string[] GetEmailTemplateBodyParameterArray(string bodyParameter)
        {
            return GetStringArray(bodyParameter, ",");
        }

        public static string CreateGUIDKey()
        {
            Guid guid = Guid.NewGuid();
            return guid.ToString();
        }

        public static string ToCDATA(string value)
        {
            if (value == null)
            {
                return "<![CDATA[]]>";
            }
            else
                return "<![CDATA[" + value + "]]>";
        }
        /// <summary>
        /// convert string to unicode number (like 78,123,56)
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string ToUnicode(string text)
        {
            byte[] stringBytes = Encoding.Unicode.GetBytes(text);
            char[] stringChars = Encoding.Unicode.GetChars(stringBytes);
            int[] intChars = stringChars.Select(x => (int)x).ToArray();

            //StringBuilder builder = new StringBuilder();
            //Array.ForEach<char>(stringChars, c => builder.AppendFormat("\\u{0:X}", (int)c));
            return string.Join(",", intChars);
        }

        public static string GetLimitedText(string text, int limitCount)
        {
            if (string.IsNullOrEmpty(text))
            {
                return string.Empty;
            }
            else
            {
                if (text.Length <= limitCount)
                {
                    return text;
                }
                else
                {
                    return text.Substring(0, limitCount) + "...";
                }
            }
        }

        public static string GetLimitedTextFromUrl(string url, int limitCount)
        {
            string destText = string.Empty;
            if (!string.IsNullOrEmpty(url))
            {
                url = url.ToUpper();
                destText = url;
                if (url.StartsWith("HTTP://") || url.StartsWith("HTTPS://"))
                {
                    destText = url.Replace("HTTP://", "").Replace("HTTPS://", "").Replace("WWW.", "");
                }
                else
                {
                    destText = url;
                }
                if (destText.Length > limitCount)
                {
                    destText = destText.Substring(0, limitCount) + "...";
                }
            }
            return destText;
        }

        public static string GetFormattedUrl(string source)
        {
            string dest = string.Empty;
            if (!string.IsNullOrEmpty(source))
            {
                string sourceTmp = source.ToUpper();
                if (sourceTmp.StartsWith("HTTP://") || sourceTmp.StartsWith("HTTPS://"))
                {
                    dest = source;
                }
                else
                {
                    dest = "http://" + source;
                }
            }
            return dest;
        }

        public static void GetFormattedUrlAndText(string source, int limitCount, ref string dest, ref string destText)
        {
            if (!string.IsNullOrEmpty(source))
            {
                string sourceTmp = source.ToUpper();
                if (sourceTmp.StartsWith("HTTP://") || sourceTmp.StartsWith("HTTPS://"))
                {
                    dest = source;
                    destText = sourceTmp.Replace("HTTP://", "").Replace("HTTPS://", "").Replace("WWW.", "");
                }
                else
                {
                    dest = "http://" + source;
                    destText = source;
                }
                if (destText.Length > limitCount)
                {
                    destText = destText.Substring(0, limitCount) + "...";
                }
            }
        }

        public static string GetDateTimeStringForRFC822(DateTime date, string timeZone)
        {
            string format = "ddd, dd MMM yyyy HH:mm:ss";
            return date.ToString(format) + " " + timeZone;
        }

        public static string AppendQueryStringValue(string url, string key, string value, Boolean onlyQueryStringInUrl = false)
        {
            if (onlyQueryStringInUrl || url.Contains("?"))
            {
                return url + "&" + key + "=" + value;
            }
            else
            {
                return url + "?" + key + "=" + value;
            }
        }

        public static string ReplaceQueryStringValue(string url, string key, string value)
        {
            return Regex.Replace(
                    url,
                //match ?key=, &key= or key=, and terminate before ?, & or #
                    @"([?&]?" + key + ")=[^?&#]+",
                    "$1=" + value);
        }

        /// <summary>
        /// delete one specific key/value pair in query string, e.g. test.com?a=1&b=2,  if you want delete a, then result: test.com?b=2
        /// </summary>
        /// <param name="url"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string DeleteQueryString(string url, string key)
        {
            if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(key))
            {
                return string.Empty;
            }
            else
            {
                int qmarkIndex = url.IndexOf('?');
                if (qmarkIndex > 0 && qmarkIndex < url.Length - 1)
                {
                    string prefix = url.Substring(0, qmarkIndex);
                    string query = url.Substring(qmarkIndex + 1);
                    string[] pairs = query.Split(new string[] { "&" }, StringSplitOptions.RemoveEmptyEntries);
                    List<string> pairsLeft = new List<string>();
                    foreach (var pair in pairs)
                    {
                        if (pair.ToLower() == key.ToLower() || pair.ToLower().StartsWith(key.ToLower() + "="))
                        {
                            continue;
                        }
                        else {
                            pairsLeft.Add(pair);
                        }
                    }
                    if (pairsLeft.Count > 0)
                    {
                        return prefix + "?" + string.Join("&", pairsLeft);
                    }
                    else
                    {
                        return prefix;
                    }
                }
                else
                {
                    return url;
                }
            }
        }

        public static string AppendOrReplaceQueryStringValue(string url, string key, string value, Boolean onlyQueryStringInUrl = false)
        {
            if (!url.Contains(key + "="))
            {
                return AppendQueryStringValue(url, key, value, onlyQueryStringInUrl);
            }
            else
            {
                return ReplaceQueryStringValue(url, key, value);
            }
        }

        public static string AppendOrReplaceQueryStringValueInHashUrl(string urlWithHash, string hashKey, string key, string value)
        {
            //in order to e compatible with old url, we should handle old Hash key (#s)
            string oldHashKey = "#s=";
            hashKey = hashKey.Contains("#") ? hashKey + "=" : "#" + hashKey + "=";
            string hashUrl = string.Empty;
            string prefixPart = string.Empty;
            int index = urlWithHash.IndexOf(hashKey);
            int length = hashKey.Length;
            if (index < 0)
            {
                index = urlWithHash.IndexOf(oldHashKey);
                length = oldHashKey.Length;
            }
            if (index > 0)
            {
                prefixPart = urlWithHash.Substring(0, index);
                hashUrl = urlWithHash.Substring(index + length);
                string replacedHashUrl = AppendOrReplaceQueryStringValue(hashUrl, key, value, true);
                return prefixPart + hashKey + replacedHashUrl;
            }
            else
            {
                //if no hashKey, append it to url 6/28/2012
                return urlWithHash + hashKey + HttpUtility.UrlEncode("&" + key + "=" + value);

                //old
                //don't append this key/value directly, since this is url with hash, if you want to append it, you should append it in hash part
                //AppendOrReplaceQueryStringValue(urlWithHash, key, value); 
            }
        }

        public static string DiscardHashKeyInQueryString(string queryString, string hashKey)
        {
            if (!string.IsNullOrEmpty(hashKey))
            {
                //in order to e compatible with old url, we should handle old Hash key (#s)
                string oldHashKey = "#s=";
                hashKey = hashKey.Contains("#") ? hashKey + "=" : "#" + hashKey + "=";
                string hashUrl = string.Empty;
                string prefixPart = string.Empty;
                int index = queryString.IndexOf(hashKey);
                int length = hashKey.Length;
                if (index < 0)
                {
                    index = queryString.IndexOf(oldHashKey);
                    length = oldHashKey.Length;
                }
                if (index > 0)
                {
                    prefixPart = queryString.Substring(0, index);
                    hashUrl = HttpUtility.UrlDecode(queryString.Substring(index + length));
                    queryString = prefixPart + hashUrl;
                }
                return queryString;
            }
            else
            {
                return queryString;
            }
        }

        /// <summary>
        /// encode query string in hash part only, if no hash part, don't encode it
        /// </summary>
        /// <param name="urlWithHash"></param>
        /// <param name="hashKey"></param>
        /// <returns></returns>
        public static string EncodeQueryStringInHashUrl(string urlWithHash, string hashKey, bool decodeHashPartFirst = true)
        {
            //in order to e compatible with old url, we should handle old Hash key (#s)
            string oldHashKey = "#s=";
            hashKey = hashKey.Contains("#") ? hashKey + "=" : "#" + hashKey + "=";
            string hashUrl = string.Empty;
            string prefixPart = string.Empty;
            int index = urlWithHash.IndexOf(hashKey);
            int length = hashKey.Length;
            if (index < 0)
            {
                index = urlWithHash.IndexOf(oldHashKey);
                length = oldHashKey.Length;
            }
            if (index > 0)
            {
                prefixPart = urlWithHash.Substring(0, index);
                hashUrl = urlWithHash.Substring(index + length);
                if (decodeHashPartFirst)
                {
                    hashUrl = HttpUtility.UrlDecode(hashUrl);
                }
                return prefixPart + hashKey + HttpUtility.UrlEncode(hashUrl);
            }
            else
            {
                return urlWithHash;
                //if don't find the hashkey in url, don't encode this url, since this is url with hash
                //HttpUtility.UrlEncode(urlWithHash);
            }
        }

        /// <summary>
        /// encode the whole url, but for url with hash, if hash part is already encoded, decoded first, and then encode the whole url
        /// </summary>
        /// <param name="urlWithHash"></param>
        /// <param name="hashKey"></param>
        /// <returns></returns>
        public static string EncodeUrlWithHash(string urlWithHash, string hashKey)
        {
            hashKey = hashKey.Contains("#") ? hashKey + "=" : "#" + hashKey + "=";
            int length = hashKey.Length;
            string hashUrl = string.Empty;
            string prefixPart = string.Empty;
            int index = urlWithHash.IndexOf(hashKey);
            if (index > 0)
            {
                prefixPart = urlWithHash.Substring(0, index);
                hashUrl = urlWithHash.Substring(index + length);
                return HttpUtility.UrlEncode(prefixPart + hashKey + HttpUtility.UrlDecode(hashUrl));
            }
            else
            {
                return HttpUtility.UrlEncode(urlWithHash);
            }
        }


        public static int GetStartRowNumber(int page, int pageSize)
        {
            if (page <= 0)
            {
                page = 1;
            }
            return 1 + pageSize * (page - 1);
        }

        public static int GetEndRowNumber(int page, int pageSize)
        {
            if (page <= 0)
            {
                page = 1;
            }
            return pageSize + pageSize * (page - 1);
        }

        public static int GetPageByRowNumber(int rowNumber, int pageSize)
        {
            if (rowNumber <= 0)
            {
                return 1;
            }
            else
            {
                return (rowNumber % pageSize == 0) ? rowNumber / pageSize : rowNumber / pageSize + 1;
            }
        }

        /// <summary>
        /// get the string for sql in clause from a int list
        /// </summary>
        /// <param name="list">List: 1 - 2 - 3</param>
        /// <returns>(1,2,3)</returns>
        public static string GetSqlInString(IEnumerable<int> list)
        {
            return "(" + string.Join(",", list) + ")";
        }


        public static void MapTuple<TM, TD1, TKey>(IEnumerable<TM> masters, IEnumerable<Tuple<TKey, TD1>> tuples, string mapPropertyName, string[] setPropertyNames)
        {
            MapTuple<TM, TD1, DontMap, DontMap, DontMap, DontMap, TKey>(masters, GetTuple(tuples), mapPropertyName, setPropertyNames);
        }
        public static void MapTuple<TM, TD1, TD2, TKey>(IEnumerable<TM> masters, IEnumerable<Tuple<TKey, TD1, TD2>> tuples, string mapPropertyName, string[] setPropertyNames)
        {
            MapTuple<TM, TD1, TD2, DontMap, DontMap, DontMap, TKey>(masters, GetTuple(tuples), mapPropertyName, setPropertyNames);
        }
        public static void MapTuple<TM, TD1, TD2, TD3, TKey>(IEnumerable<TM> masters, IEnumerable<Tuple<TKey, TD1, TD2, TD3>> tuples, string mapPropertyName, string[] setPropertyNames)
        {
            MapTuple<TM, TD1, TD2, TD3, DontMap, DontMap, TKey>(masters, GetTuple(tuples), mapPropertyName, setPropertyNames);
        }
        public static void MapTuple<TM, TD1, TD2, TD3, TD4, TKey>(IEnumerable<TM> masters, IEnumerable<Tuple<TKey, TD1, TD2, TD3, TD4>> tuples, string mapPropertyName, string[] setPropertyNames)
        {
            MapTuple<TM, TD1, TD2, TD3, TD4, DontMap, TKey>(masters, GetTuple(tuples), mapPropertyName, setPropertyNames);
        }

        private static IEnumerable<Tuple<TKey, TD1, DontMap, DontMap, DontMap, DontMap>> GetTuple<TKey, TD1>(IEnumerable<Tuple<TKey, TD1>> tuples)
        {
            return tuples.Select<Tuple<TKey, TD1>, Tuple<TKey, TD1, DontMap, DontMap, DontMap, DontMap>>(
                x => new Tuple<TKey, TD1, DontMap, DontMap, DontMap, DontMap>(
                    x.Item1,
                    x.Item2,
                    new DontMap(),
                    new DontMap(),
                    new DontMap(),
                    new DontMap()
                    )
                ).AsEnumerable();
        }
        private static IEnumerable<Tuple<TKey, TD1, TD2, DontMap, DontMap, DontMap>> GetTuple<TKey, TD1, TD2>(IEnumerable<Tuple<TKey, TD1, TD2>> tuples)
        {
            return tuples.Select<Tuple<TKey, TD1, TD2>, Tuple<TKey, TD1, TD2, DontMap, DontMap, DontMap>>(
                x => new Tuple<TKey, TD1, TD2, DontMap, DontMap, DontMap>(
                    x.Item1,
                    x.Item2,
                    x.Item3,
                    new DontMap(),
                    new DontMap(),
                    new DontMap()
                    )
                ).AsEnumerable();
        }
        private static IEnumerable<Tuple<TKey, TD1, TD2, TD3, DontMap, DontMap>> GetTuple<TKey, TD1, TD2, TD3>(IEnumerable<Tuple<TKey, TD1, TD2, TD3>> tuples)
        {
            return tuples.Select<Tuple<TKey, TD1, TD2, TD3>, Tuple<TKey, TD1, TD2, TD3, DontMap, DontMap>>(
                x => new Tuple<TKey, TD1, TD2, TD3, DontMap, DontMap>(
                    x.Item1,
                    x.Item2,
                    x.Item3,
                    x.Item4,
                    new DontMap(),
                    new DontMap()
                    )
                ).AsEnumerable();
        }
        private static IEnumerable<Tuple<TKey, TD1, TD2, TD3, TD4, DontMap>> GetTuple<TKey, TD1, TD2, TD3, TD4>(IEnumerable<Tuple<TKey, TD1, TD2, TD3, TD4>> tuples)
        {
            return tuples.Select<Tuple<TKey, TD1, TD2, TD3, TD4>, Tuple<TKey, TD1, TD2, TD3, TD4, DontMap>>(
                x => new Tuple<TKey, TD1, TD2, TD3, TD4, DontMap>(
                    x.Item1,
                    x.Item2,
                    x.Item3,
                    x.Item4,
                    x.Item5,
                    new DontMap()
                    )
                ).AsEnumerable();
        }

        /// <summary>
        /// dynamically map the details info TD1, TD2...TD5 in tuple into the master T1 by TKey as key
        /// </summary>
        /// <typeparam name="TM">the type of master object</typeparam>
        /// <typeparam name="TD1">the type of detail 1 object</typeparam>
        /// <typeparam name="TD2">the type of detail 2 object</typeparam>
        /// <typeparam name="TD3">the type of detail 3 object</typeparam>
        /// <typeparam name="TD4">the type of detail 4 object</typeparam>
        /// <typeparam name="TD5">the type of detail 5 object</typeparam>
        /// <typeparam name="TKey">the type of key which connect master object and detail object</typeparam>
        /// <param name="masters">a list of master data</param>
        /// <param name="tuples">a list of tuple of details object</param>
        /// <param name="mapPropertyName">the property name (which type is TKey) which connect master and details</param>
        /// <param name="setPropertyNames">the property names in master object. (detail 1 maps the first, detail 2 maps the second property name in this array)</param>
        public static void MapTuple<TM, TD1, TD2, TD3, TD4, TD5, TKey>(IEnumerable<TM> masters, IEnumerable<Tuple<TKey, TD1, TD2, TD3, TD4, TD5>> tuples, string mapPropertyName, string[] setPropertyNames)
        {
            int detailCount = 0;
            if (typeof(TD1) != typeof(DontMap))
            {
                detailCount++;
            }
            if (typeof(TD2) != typeof(DontMap))
            {
                detailCount++;
            }
            if (typeof(TD3) != typeof(DontMap))
            {
                detailCount++;
            }
            if (typeof(TD4) != typeof(DontMap))
            {
                detailCount++;
            }
            if (typeof(TD5) != typeof(DontMap))
            {
                detailCount++;
            }
            if (setPropertyNames.Length != detailCount)
            {
                throw new Exception("Parameters' count doesn't match!");
            }

            PropertyInfo p = typeof(TM).GetProperty(mapPropertyName);
            List<PropertyInfo> setpList = new List<PropertyInfo>();
            List<bool> setpFlagList = new List<bool>();
            string masterClassName = typeof(TM).Name;
            foreach (string pName in setPropertyNames)
            {
                PropertyInfo setp = typeof(TM).GetProperty(pName);
                bool setpIsList = false;
                if (setp == null)
                {
                    throw new Exception(string.Format("Property '{0}' doesn't exist in class '{1}'", pName, masterClassName));
                }
                if (setp.PropertyType.IsGenericType &&
                (setp.PropertyType.FullName.Contains("System.Collections.Generic.List") ||
                setp.PropertyType.FullName.Contains("System.Collections.Generic.IEnumerable")))
                {
                    setpIsList = true;
                }
                setpList.Add(setp);
                setpFlagList.Add(setpIsList);
            }

            foreach (TM master in masters)
            {
                TKey keyvalue = (TKey)p.GetValue(master, null);

                #region init list if it's
                List<IList> q = new List<IList>();
                for (int i = 0; i < setpList.Count; i++)
                {
                    if (setpFlagList[i])
                    {
                        switch (i)
                        {
                            case 0:
                                q.Add((IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(TD1))));
                                break;
                            case 1:
                                q.Add((IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(TD2))));
                                break;
                            case 2:
                                q.Add((IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(TD3))));
                                break;
                            case 3:
                                q.Add((IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(TD4))));
                                break;
                            case 4:
                                q.Add((IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(TD5))));
                                break;
                        }
                    }
                    else
                    {
                        q.Add(null);
                    }
                }
                #endregion

                foreach (var tuple in tuples)
                {
                    TKey tupleValue = tuple.Item1;
                    if (tupleValue.Equals(keyvalue))
                    {
                        #region set value to list or property directly
                        for (int i = 0; i < setpList.Count; i++)
                        {
                            if (setpFlagList[i] && q[i] != null)
                            {
                                switch (i)
                                {
                                    case 0:
                                        q[i].Add(tuple.Item2);
                                        break;
                                    case 1:
                                        q[i].Add(tuple.Item3);
                                        break;
                                    case 2:
                                        q[i].Add(tuple.Item4);
                                        break;
                                    case 3:
                                        q[i].Add(tuple.Item5);
                                        break;
                                    case 4:
                                        q[i].Add(tuple.Item6);
                                        break;
                                }
                            }
                            else
                            {
                                switch (i)
                                {
                                    case 0:
                                        setpList[i].SetValue(master, tuple.Item2, null);
                                        break;
                                    case 1:
                                        setpList[i].SetValue(master, tuple.Item3, null);
                                        break;
                                    case 2:
                                        setpList[i].SetValue(master, tuple.Item4, null);
                                        break;
                                    case 3:
                                        setpList[i].SetValue(master, tuple.Item5, null);
                                        break;
                                    case 4:
                                        setpList[i].SetValue(master, tuple.Item6, null);
                                        break;
                                }

                            }
                        }
                        #endregion
                    }
                }

                for (int i = 0; i < setpList.Count; i++)
                {
                    if (setpFlagList[i] && q[i] != null)
                    {
                        setpList[i].SetValue(master, q[i], null);
                    }
                }
                q = null;
            }

            setpList = null;
            setpFlagList = null;

        }
        /// <summary>
        /// dynamically map the details info T2 in tuple with T1 by TValue as key
        /// </summary>
        /// <typeparam name="T1">master record Type</typeparam>
        /// <typeparam name="T2">detail record Type</typeparam>
        /// <typeparam name="TValue"> the type of associated key value, generally it's int<</typeparam>
        /// <param name="masters">the list of records from master table</param>
        /// <param name="tuples">the list of records from detail table, TValue is type of associated key value, generally it's int</param>
        /// <param name="mapPropertyName">the TValue maps to which property name in class T1</param>
        /// <param name="setPropertyName">set value of Type T2 to which property name in class T1</param>
        public static void MapTuple<T1, T2, TValue>(IEnumerable<T1> masters, IEnumerable<Tuple<TValue, T2>> tuples, string mapPropertyName, string setPropertyName)
        {
            PropertyInfo p = typeof(T1).GetProperty(mapPropertyName);
            PropertyInfo setp = typeof(T1).GetProperty(setPropertyName);
            Boolean setpIsList = false;

            if (p == null)
            {
                throw new Exception(string.Format("Property '{0}' doesn't exist in class '{1}'", mapPropertyName, typeof(T1).Name));
            }
            if (setp == null)
            {
                throw new Exception(string.Format("Property '{0}' doesn't exist in class '{1}'", setPropertyName, typeof(T1).Name));
            }
            if (p.PropertyType != typeof(TValue))
            {
                throw new Exception(string.Format("The type of Property '{0}' in class '{1}' must be '{2}'", mapPropertyName, typeof(T1).Name, typeof(TValue).Name));
            }

            if (setp.PropertyType.IsGenericType &&
                (setp.PropertyType.FullName.Contains("System.Collections.Generic.List") ||
                setp.PropertyType.FullName.Contains("System.Collections.Generic.IEnumerable")))
            {
                setpIsList = true;
            }

            foreach (T1 master in masters)
            {
                TValue keyvalue = (TValue)p.GetValue(master, null);
                IList list = null;
                if (setpIsList)
                {
                    list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T2)));
                }
                foreach (var tuple in tuples)
                {
                    TValue tupleValue = tuple.Item1;
                    if (tupleValue.Equals(keyvalue))
                    {
                        if (setpIsList)
                        {
                            list.Add(tuple.Item2);
                        }
                        else
                        {
                            setp.SetValue(master, tuple.Item2, null);
                        }
                    }
                }
                if (setpIsList)
                {
                    setp.SetValue(master, list, null);
                }

            }
        }

        public static T BindModelWithQueryString<T>(string queryString, string hashKey)
        {
            int index = queryString.IndexOf('?');
            if (index >= 0)
            {
                queryString = queryString.Substring(index + 1);
            }
            queryString = DiscardHashKeyInQueryString(queryString, hashKey);

            //object[] args = new object[] { queryString };
            T result = Activator.CreateInstance<T>();
            string[] splits = queryString.Split('&');
            foreach (string split in splits)
            {
                if (split != string.Empty && split.Contains("="))
                {
                    int index2 = split.IndexOf('=');

                    string key = split.Substring(0, index2).ToLower();

                    PropertyInfo p = (from pp in typeof(T).GetProperties() where pp.Name.ToLower() == key select pp).SingleOrDefault();
                    if (p != null)
                    {
                        object value = split.Substring(index2 + 1); //ConvertValueToPropertyType(split.Substring(index2 + 1),  p.PropertyType);
                        p.SetValue(result, Convert.ChangeType(value, p.PropertyType), null);
                    }

                }
            }

            //special property for searching
            PropertyInfo specialProp = (from pp in typeof(T).GetProperties() where pp.Name.ToLower() == "querystring" select pp).SingleOrDefault();
            if (specialProp != null)
            {
                specialProp.SetValue(result, Convert.ChangeType(queryString, specialProp.PropertyType), null);
            }

            return result;
        }

        //public static string JoinString(string str1, string str2, string separator)
        //{
        //    if (!string.IsNullOrEmpty(str1) && !string.IsNullOrEmpty(str2))
        //    {
        //        return str1 + separator + str2;
        //    }
        //    else if (!string.IsNullOrEmpty(str1))
        //    {
        //        return str1;
        //    }
        //    else if (!string.IsNullOrEmpty(str2))
        //    {
        //        return str2;
        //    }
        //    else
        //        return string.Empty;
        //}

        public static string stripPunctuation(string field)
        {
            string modField = replaceText(field, "[^a-z0-9]", " ", true);
            modField = replaceText(modField, "\\s+", " ", true);
            return modField;
        }

        public static string stripNoiseWords(string field)
        {
            //string modField = replaceText(field, @"\b(?!(the|a|an|and|or|on|of|with)\b)\w+", "", true);
            string modField = replaceText(field, "\ba\b|\ban\b|\band\b|\bthe\b|\bof\b|\bto\b|\bfor\b", "", true);
            modField = replaceText(modField, "\\s+", " ", true);
            modField = replaceText(modField, "^\\s+|\\s+$", " ", true);
            return modField;
        }

        public static string replaceText(string srcTxt, string findTxt, string replTxt, bool igCase)
        {
            Regex myRegExp = new Regex(findTxt, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            string rText = myRegExp.Replace(srcTxt, replTxt);
            return rText;
        }
    }
}
